﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Logging;
#if NETFX_CORE
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;

#else
using System.Windows;
using System.Windows.Data;

#endif

namespace StyleMVVM.Utilities
{
	public delegate void DataContextChangedHandler(FrameworkElement element, object oldContext, object newContext);

	public sealed class DataContextChangedHelper
	{
		private static readonly string supplemental = typeof(DataContextChangedHelper).FullName;

		internal static object GetDataContext(DependencyObject obj)
		{
			return (object)obj.GetValue(DataContextProperty);
		}

		internal static void SetDataContext(DependencyObject obj, object value)
		{
			obj.SetValue(DataContextProperty, value);
		}

		// Using a DependencyProperty as the backing store for DataContext.  This enables animation, styling, binding, etc...
		internal static readonly DependencyProperty DataContextProperty =
			DependencyProperty.RegisterAttached("DataContext",
			                                    typeof(object),
			                                    typeof(DataContextChangedHelper),
			                                    new PropertyMetadata(null, DataContextChanged));

		private static void DataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			DataContextChangedHandler handler = GetContextChangeHandlers(d);

			if (handler != null)
			{
				try
				{
					handler(d as FrameworkElement, e.OldValue, e.NewValue);
				}
				catch (Exception exp)
				{
					Logger.Error("Excpetion thrown while executing data context change handler", supplemental, exp);
				}
			}
		}

		internal static DataContextChangedHandler GetContextChangeHandlers(DependencyObject obj)
		{
			return (DataContextChangedHandler)obj.GetValue(ContextChangeHandlersProperty);
		}

		internal static void SetContextChangeHandlers(DependencyObject obj, DataContextChangedHandler value)
		{
			obj.SetValue(ContextChangeHandlersProperty, value);
		}

		// Using a DependencyProperty as the backing store for ContextChangeHandlers.  This enables animation, styling, binding, etc...
		internal static readonly DependencyProperty ContextChangeHandlersProperty =
			DependencyProperty.RegisterAttached("ContextChangeHandlers",
			                                    typeof(DataContextChangedHandler),
			                                    typeof(DataContextChangedHelper),
			                                    new PropertyMetadata(null));

		public static void RegisterDataContextChangeHandler(DependencyObject obj, DataContextChangedHandler handler)
		{
			FrameworkElement element = obj as FrameworkElement;

			if (element != null)
			{
				if (GetDataContext(obj) == null)
				{
					element.SetBinding(DataContextProperty,
					                   new Binding());
				}

				DataContextChangedHandler oldHandler = GetContextChangeHandlers(obj);

				oldHandler += handler;

				SetContextChangeHandlers(obj, oldHandler);
			}
		}

		public static void UnregisterDataContextChangeHandler(DependencyObject obj, DataContextChangedHandler handler)
		{
			FrameworkElement element = obj as FrameworkElement;

			if (element != null)
			{
				DataContextChangedHandler oldHandler = GetContextChangeHandlers(obj);

				oldHandler -= handler;

				if (oldHandler == null)
				{
					element.ClearValue(DataContextProperty);
					element.ClearValue(ContextChangeHandlersProperty);
				}
				else
				{
					SetContextChangeHandlers(obj, oldHandler);
				}
			}
		}
	}
}